package com.bridgeintelligent.tag.engine.mvc.service.impl;

import com.bridgeintelligent.tag.engine.api.DimItemInfo;
import com.bridgeintelligent.tag.engine.api.EngineException;
import com.bridgeintelligent.tag.engine.api.IndexNameInfo;
import com.bridgeintelligent.tag.engine.api.SortModel;
import com.bridgeintelligent.tag.engine.api.rsp.DimMonitorDetailRsp;
import com.bridgeintelligent.tag.engine.api.rsp.Page;
import com.bridgeintelligent.tag.engine.api.rsp.SetModel;
import com.bridgeintelligent.tag.engine.core.cache.CacheConfig;
import com.bridgeintelligent.tag.engine.core.cache.CacheUtils;
import com.bridgeintelligent.tag.engine.es.base.ESQueryUtils;
import com.bridgeintelligent.tag.engine.es.model.DimMonitorDetailModel;
import com.bridgeintelligent.tag.engine.mvc.service.IDimMonitorDetailService;
import com.bridgeintelligent.tag.engine.mvc.service.IEsDateService;
import com.bridgeintelligent.tag.engine.mvc.service.IMonitorService;
import com.bridgeintelligent.tag.engine.mvc.service.TermsUtils;
import com.bridgeintelligent.tag.engine.parse.BaseParse;
import com.wayneleo.quickstart.framework.ExceptionCode;
import lombok.*;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.elasticsearch.action.search.MultiSearchRequest;
import org.elasticsearch.action.search.MultiSearchResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.range.Range;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @description 标签历史监控详情
 * @author: grp 2020-10-12 13:38
 **/
@Service
@Slf4j
public class DimMonitorDetailServiceImpl implements IDimMonitorDetailService, IMonitorService {

    private static final String TOTAL_TITLE_STR = "全部";

    @Autowired
    private IEsDateService esDateService;

    @Override
    public DimMonitorDetailRsp discrete(DimMonitorDetailModel model) throws IOException {
        if (StringUtils.equals(model.getSortDate(), "title")) {
            return defaultResult(model);
        }
        if (StringUtils.isNotBlank(model.getSortDate())) {
            return sortResult(model);
        }
        return defaultResult(model);
    }

    @Override
    public DimMonitorDetailRsp defaultGroup(DimMonitorDetailModel model) throws IOException {
        if ("06".equals(model.getType())) {
            return getDetailResult(model);
        } else {
            if (StringUtils.equals(model.getSortDate(), "title")) {
                return getGroupResult(model);
            }
            if (StringUtils.isNotBlank(model.getSortDate())) {
                return getGroupSortResult(model);
            }
            return getGroupResult(model);
        }


    }

    public DimMonitorDetailRsp getDetailResult(DimMonitorDetailModel model) {
        Map<String, String> timeMap = esDateService.timeSymbolRelation("1");
        timeMap = Optional.ofNullable(timeMap).orElse(new HashMap<>());
        //先封装多个request
        List<String> dates = model.getDates();
        String dimId = model.getDimId();
        List<Map<String, Object>> datalist = new ArrayList<>();
        SetModel setModel = treeSet(model.getDimId(),model.getKeys());
        IndexNameInfo indexNameInfo = CacheUtils.getCacheInfo(CacheConfig.CONFIGURATION_INFO, "1");
        MultiSearchRequest request = new MultiSearchRequest();
        for (String date : dates) {
            date = Optional.ofNullable(timeMap.get(date)).orElse(date);
            request.add(getListSearchRequest(indexNameInfo, dimId, date));
        }
        try {
            Map<String, Result> resultMap = getListStringResultMap(model, setModel.getTitles(),request);
            listData( model,  setModel.getTitles(), resultMap, datalist);
            //表头数据
            List<Map<String, Object>> totalList = createTotalRow(model.getDates(), resultMap);
            //排序数据
            List<Map<String, Object>> realList = sortList(datalist, totalList, model);
            //返回分页数据
            return pageData( realList, model);

        } catch (IOException e) {
            log.info("---------查询ES出现异常------------",e);
            throw new EngineException(new ExceptionCode(10002,"查询ES出现异常"),e);
        }

    }

    private SetModel treeSet(String dimId, List<String> keys){
        Set<String> titles = new TreeSet<>(Comparator.naturalOrder());
        Set<String> newTitles = new TreeSet<>(Comparator.naturalOrder());
        keys.stream().forEach(e ->{
            titles.add(e);
            String title = Optional
                    .ofNullable((DimItemInfo) CacheUtils.getCacheInfo(CacheConfig.DIM_ITEM_INFO,
                            String.format(BaseParse.GROUP_FORMAT, dimId, e)))
                    .map(DimItemInfo::getDimItemName)
                    .orElse(e);
            newTitles.add(joinStr(title,e));

        });
        return new SetModel(titles,newTitles);
    }

    /**
     * 根据规则排序
     *
     * @param dataList
     * @param totalList
     * @param model
     * @return
     */
    public List<Map<String, Object>> sortList(List<Map<String, Object>> dataList, List<Map<String, Object>> totalList, DimMonitorDetailModel model) {
        boolean sortType = model.getSort();
        String sortKey = model.getSortDate();
        //处理排序字段
        List<SortModel> newList = new ArrayList<>(dataList.size());
        dataList.stream().forEach(m -> {
            SortModel sortModel = null;
            if (m.containsKey(sortKey)) {
                sortModel = new SortModel();
                if ("title".equals(sortKey)) {
                    sortModel.setSortKey(m.get(sortKey).toString());
                } else {
                    DimMonitorDetailRsp.Info info = (DimMonitorDetailRsp.Info) m.get(sortKey);
                    sortModel.setSortKey(info.getCount());
                }
                sortModel.setSortValue(m);
                newList.add(sortModel);
            }
        });
        //处理排序方式
        List<SortModel> oldList = null;
        if (sortType) {
            //升序
            oldList = newList.stream().sorted(Comparator.comparing(SortModel::getSortKey)).collect(Collectors.toList());
        } else {
            //降序
            oldList = newList.stream().sorted(Comparator.comparing(SortModel::getSortKey).reversed()).collect(Collectors.toList());
        }
        List<Map<String, Object>> sortList = oldList.stream().map(SortModel::getSortValue).collect(Collectors.toList());
        List<Map<String, Object>> realList = new ArrayList<>(dataList.size() + totalList.size());
        realList.addAll(totalList);
        realList.addAll(sortList);
        return realList;
    }

    private void listData(DimMonitorDetailModel model, Set<String> titles, Map<String, Result> map, List<Map<String, Object>> list) {
        for (String title : titles) {
            // 查询到请求页
            Map<String, Object> row = new HashMap<>();
            String showName = Optional
                    .ofNullable((DimItemInfo) CacheUtils.getCacheInfo(CacheConfig.DIM_ITEM_INFO,
                            String.format(BaseParse.GROUP_FORMAT, model.getDimId(), title)))
                    .map(DimItemInfo::getDimItemName)
                    .orElse(title);
            row.put("title", joinStr(showName,title));
            row.put("itemNo",title);
            for (String date : model.getDates()) {
                Result result = map.get(date);
                Terms.Bucket bucket = result.getMapping().get(title);
                if (bucket == null) {
                    row.put(date, getDefaultInfo("0", "0%"));
                } else {
                    DimMonitorDetailRsp.Info info = getDefaultInfo(String.valueOf(bucket.getDocCount()), TermsUtils.getPercent(bucket.getDocCount(),
                            result.getTotal()));
                    row.put(date, info);
                }
            }
            list.add(row);
        }
    }

    private List<Map<String, Object>> createTotalRow(List<String> dates, Map<String, Result> map) {
        List<Map<String, Object>> data = new ArrayList<>(1);
        Map<String, Object> row = new HashMap<>();
        row.put("title", TOTAL_TITLE_STR);
        for (String date : dates) {
            Result result = map.get(date);
            DimMonitorDetailRsp.Info info = getDefaultInfo(result.getTotal() + "", "100.00%");
            row.put(date, info);
        }
        data.add(row);
        return  data;
    }

    private SearchRequest getListSearchRequest(IndexNameInfo indexNameInfo, String dimId, String date) {
        String dimIdColumn = getDimIdColumn(dimId, date);
        BoolQueryBuilder boolQueryBuilder = getBoolQueryBuilder(dimIdColumn);
        TermsAggregationBuilder termsBuilder = AggregationBuilders.terms("count")
                .field(dimIdColumn)
                .order(BucketOrder.key(true))
                .size(Integer.MAX_VALUE);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.aggregation(termsBuilder).query(boolQueryBuilder).trackTotalHits(true).size(0);
        return new SearchRequest(indexNameInfo.getDataDate()).types(indexNameInfo.getIndexType())
                .source(searchSourceBuilder);
    }

    private Map<String, Result> getListStringResultMap(DimMonitorDetailModel model, Set<String> titles,MultiSearchRequest request) throws IOException {
        Map<String, Result> map = new HashMap<>();
        int sort = 0;
        MultiSearchResponse multiSearchResponse = ESQueryUtils.getClient().multiSearch(request, RequestOptions.DEFAULT);
        for (MultiSearchResponse.Item item : multiSearchResponse.getResponses()) {
            String date = model.getDates().get(sort++);
            SearchResponse response = item.getResponse();
            SearchHits hits = response.getHits();
            Result result = new Result();
            result.setMapping(new HashMap<>());
            result.setTotal(hits.getTotalHits().value);
            Aggregations aggregations = response.getAggregations();
            Terms terms = aggregations.get("count");
            int num = 0;
            for (Terms.Bucket entry : terms.getBuckets()) {
                if (titles.contains(entry.getKeyAsString())){
                    result.getMapping().put(entry.getKeyAsString(), entry);
                    num = num + Integer.valueOf(String.valueOf(entry.getDocCount()));
                }
            }
            result.setTotal(num);
            map.put(date, result);
        }
        return map;
    }

    public DimMonitorDetailRsp pageData(List<Map<String, Object>> realList, DimMonitorDetailModel model) {
        if (realList.size() == 1) {
            Page<Map<String, Object>> page = Page.<Map<String, Object>>of().setContent(Collections.emptyList())
                    .setSize(model.getPageSize())
                    .setRequestPager(model.getPageNumber())
                    .setTotalElements(0L)
                    .setTotalPages(0);
            return DimMonitorDetailRsp.of().setData(page);
        }
        int pages = realList.size() / model.getPageSize();
        List<Map<String, Object>> pageList = realList.stream().skip((model.getPageNumber() - 1) * model.getPageSize()).limit(model.getPageSize()).collect(Collectors.toList());
        Page<Map<String, Object>> page = Page.<Map<String, Object>>of()
                .setContent(pageList)
                .setSize(model.getPageSize())
                .setRequestPager(model.getPageNumber())
                .setTotalElements((long) realList.size())
                .setTotalPages(realList.size() % model.getPageSize() > 0 ? pages + 1 : pages);
        return DimMonitorDetailRsp.of().setData(page);
    }


    public String joinStr(String key,String value){
        StringBuffer buffer = new StringBuffer();
        buffer.append("").append(key).append("(").append(value).append(")");
        return buffer.toString();
    }


    @Override
    public DimMonitorDetailRsp customize(DimMonitorDetailModel model) throws IOException {
        if (StringUtils.equals(model.getSortDate(), "title")) {
            return getCustomizeResult(model);
        }
        if (StringUtils.isNotBlank(model.getSortDate())) {
            return getCustomizeSortResult(model);
        }
        return getCustomizeResult(model);
    }

    /**
     * 按照某一个日期排序
     *
     * @param model 输入
     * @return 响应结果
     */
    private DimMonitorDetailRsp sortResult(DimMonitorDetailModel model) throws IOException {
        LinkedList<String> titles = new LinkedList<>();
        Set<String> others = new TreeSet<>(Comparator.reverseOrder());
        // 获取统计结果
        Map<String, Result> map = getStringResultMapByOrder(model, titles, others);
        // 分页
        int start = getStart(model);
        int end = model.getPageNumber() * model.getPageSize() - 1;
        List<Map<String, Object>> data = new ArrayList<>();
        createTotalRow(model, map, data);
        int number = getData(model, titles, map, 0, start, end, data);
        getData(model, others, map, number, start, end, data);
        int total = titles.size() + others.size() + 1;
        return getDimMonitorDetailRsp(model, data, total);
    }


    /**
     * 按照某一个日期排序
     *
     * @param model 输入
     * @return
     */
    private DimMonitorDetailRsp getGroupSortResult(DimMonitorDetailModel model) throws IOException {
        LinkedList<String> titles = new LinkedList<>();
        Set<String> others = new TreeSet<>(Comparator.reverseOrder());
        // 获取统计结果
        Map<String, HistogramResult> map = getGroupStringResultMapping(model, titles, others);
        // 分页
        int start = getStart(model);
        int end = model.getPageNumber() * model.getPageSize() - 1;
        List<Map<String, Object>> data = new ArrayList<>();
        createGroupTotalRow(model, map, data);
        boolean desc = model.getSort() != null && !model.getSort();
        if (desc) {
            int number = getGroupData(model, titles, map, 0, start, end, data);
            getGroupData(model, others, map, number, start, end, data);
        } else {
            int number = getGroupData(model, others, map, 0, start, end, data);
            getGroupData(model, titles, map, number, start, end, data);
        }
        int total = titles.size() + others.size() + 1;
        return getDimMonitorDetailRsp(model, data, total);
    }

    /**
     * 按照某一个日期排序
     *
     * @param model 输入
     * @return
     */
    private DimMonitorDetailRsp getCustomizeSortResult(DimMonitorDetailModel model) throws IOException {
        Set<SortResult> titles = getSortResultsByCount(model);
        Set<SortResult> others = getSortResultsByName(model);
        Map<String, RangeResult> map = getCustomizeResultMapping(model, titles, others);
        // 分页
        int start = getStart(model);
        int end = model.getPageNumber() * model.getPageSize() - 1;
        List<Map<String, Object>> data = new ArrayList<>();
        createCustomizeTotalRow(model, map, data);
        boolean desc = model.getSort() != null && !model.getSort();
        if (desc) {
            // 降序
            int number = getCustomizeData(model, titles, map, 0, start, end, data);
            getCustomizeData(model, others, map, number, start, end, data);
        } else {
            // 升序
            int number = getCustomizeData(model, others, map, 0, start, end, data);
            getCustomizeData(model, titles, map, number, start, end, data);
        }
        int total = titles.size() + others.size() + 1;
        return getDimMonitorDetailRsp(model, data, total);
    }

    /**
     * 根据日期获取title和结果映射
     *
     * @param model
     * @param titles
     * @param others
     * @return
     * @throws IOException
     */
    private Map<String, HistogramResult> getGroupStringResultMapping(DimMonitorDetailModel model,
                                                                     LinkedList<String> titles, Set<String> others) throws IOException {
        IndexNameInfo indexNameInfo = CacheUtils.getCacheInfo(CacheConfig.CONFIGURATION_INFO, "1");
        Map<String, HistogramResult> map = new HashMap<>();
        // 创建请求信息
        MultiSearchRequest request = new MultiSearchRequest();
        if (StringUtils.equals("03", model.getType())) {
            getNumberSearchRequest(model, indexNameInfo, request);
        }
        if (StringUtils.equals("05", model.getType())) {
            Map<String, String> timeMap = esDateService.timeSymbolRelation("1");
            timeMap = Optional.ofNullable(timeMap).orElse(new HashMap<>());
            String sortDate = Optional.ofNullable(timeMap.get(model.getSortDate())).orElse(model.getSortDate());
            request.add(getDateSearchRequest(indexNameInfo, model, sortDate));
            for (String date : model.getDates()) {
                if (!StringUtils.equals(date, model.getSortDate())) {
                    date = Optional.ofNullable(timeMap.get(date)).orElse(date);
                    request.add(getDateSearchRequest(indexNameInfo, model, date));
                }
            }
        }
        // 处理响应
        int sort = 0;
        boolean first = true;
        MultiSearchResponse multiSearchResponse =
                ESQueryUtils.getClient().multiSearch(request, RequestOptions.DEFAULT);
        for (MultiSearchResponse.Item item : multiSearchResponse.getResponses()) {
            SearchResponse response = item.getResponse();
            HistogramResult result = new HistogramResult();
            result.setMapping(new HashMap<>());
            SearchHits hits = response.getHits();
            result.setTotal(hits.getTotalHits().value);
            // 第一个一定是排序列结果
            if (first) {
                Aggregations aggregations = response.getAggregations();
                Histogram terms = aggregations.get("count");
                for (Histogram.Bucket entry : terms.getBuckets()) {
                    result.getMapping().put(entry.getKeyAsString(), entry);
                    titles.add(entry.getKeyAsString());
                }
                map.put(model.getSortDate(), result);
                first = false;
                continue;
            }
            // 第一次已经处理了排序列 剩下的接着处理就行
            String date = model.getDates().get(sort++);
            if (StringUtils.equals(date, model.getSortDate())) {
                date = model.getDates().get(sort++);
            }
            Aggregations aggregations = response.getAggregations();
            Histogram terms = aggregations.get("count");
            for (Histogram.Bucket entry : terms.getBuckets()) {
                result.getMapping().put(entry.getKeyAsString(), entry);
                if (!titles.contains(entry.getKeyAsString())) {
                    others.add(entry.getKeyAsString());
                }
            }
            map.put(date, result);
        }
        return map;
    }

    private void getNumberSearchRequest(DimMonitorDetailModel model, IndexNameInfo indexNameInfo, MultiSearchRequest request) {

        Map<String, String> timeMap = esDateService.timeSymbolRelation("1");
        timeMap = Optional.ofNullable(timeMap).orElse(new HashMap<>());
        String sortDate = Optional.ofNullable(timeMap.get(model.getSortDate())).orElse(model.getSortDate());
        request.add(getNumberSearchRequest(indexNameInfo, model, sortDate));
        for (String date : model.getDates()) {
            if (!StringUtils.equals(date, model.getSortDate())) {
                date = Optional.ofNullable(timeMap.get(date)).orElse(date);
                request.add(getNumberSearchRequest(indexNameInfo, model, date));
            }
        }
    }


    private Map<String, Result> getStringResultMapByOrder(DimMonitorDetailModel model, LinkedList<String> titles, Set<String> others) throws IOException {
        Map<String, String> timeMap = esDateService.timeSymbolRelation("1");
        timeMap = Optional.ofNullable(timeMap).orElse(new HashMap<>());
        IndexNameInfo indexNameInfo = CacheUtils.getCacheInfo(CacheConfig.CONFIGURATION_INFO, "1");
        Map<String, Result> map = new HashMap<>();
        // 创建请求信息
        MultiSearchRequest request = new MultiSearchRequest();
        String sortDate = Optional.ofNullable(timeMap.get(model.getSortDate())).orElse(model.getSortDate());
        request.add(getSearchRequest(indexNameInfo, model, sortDate));
        for (String date : model.getDates()) {
            if (!StringUtils.equals(date, model.getSortDate())) {
                date = Optional.ofNullable(timeMap.get(date)).orElse(date);
                request.add(getSearchRequest(indexNameInfo, model, date));
            }
        }
        // 处理响应
        int sort = 0;
        boolean first = true;
        MultiSearchResponse multiSearchResponse =
                ESQueryUtils.getClient().multiSearch(request, RequestOptions.DEFAULT);
        for (MultiSearchResponse.Item item : multiSearchResponse.getResponses()) {
            SearchResponse response = item.getResponse();
            Result result = new Result();
            result.setMapping(new HashMap<>());
            SearchHits hits = response.getHits();
            result.setTotal(hits.getTotalHits().value);
            // 第一个一定是排序列结果
            if (first) {
                Aggregations aggregations = response.getAggregations();
                Terms terms = aggregations.get("count");
                for (Terms.Bucket entry : terms.getBuckets()) {
                    result.getMapping().put(entry.getKeyAsString(), entry);
                    titles.add(entry.getKeyAsString());
                }
                map.put(model.getSortDate(), result);
                first = false;
                continue;
            }
            // 第一次已经处理了排序列 剩下的接着处理就行
            String date = model.getDates().get(sort++);
            if (StringUtils.equals(date, model.getSortDate())) {
                date = model.getDates().get(sort++);
            }
            Aggregations aggregations = response.getAggregations();
            Terms terms = aggregations.get("count");
            for (Terms.Bucket entry : terms.getBuckets()) {
                result.getMapping().put(entry.getKeyAsString(), entry);
                if (!titles.contains(entry.getKeyAsString())) {
                    others.add(entry.getKeyAsString());
                }
            }
            map.put(date, result);
        }
        return map;
    }

    private Map<String, RangeResult> getCustomizeResultMapping(DimMonitorDetailModel model, Collection<SortResult> titles,
                                                               Collection<SortResult> others) throws IOException {
        Map<String, String> timeMap = esDateService.timeSymbolRelation("1");
        timeMap = Optional.ofNullable(timeMap).orElse(new HashMap<>());
        IndexNameInfo indexNameInfo = CacheUtils.getCacheInfo(CacheConfig.CONFIGURATION_INFO, "1");
        Map<String, RangeResult> map = new HashMap<>();
        Set<String> sorted = new HashSet<>();
        // 创建请求信息
        MultiSearchRequest request = new MultiSearchRequest();
        if (StringUtils.equals("03", model.getType())) {
            String sortDate = Optional.ofNullable(timeMap.get(model.getSortDate())).orElse(model.getSortDate());
            String dimIdColumn = getDimIdColumn(model.getDimId(), sortDate);
            request.add(getCustomizeNumberSearchRequest(indexNameInfo, dimIdColumn, model.getItems()));
            for (String date : model.getDates()) {
                if (!StringUtils.equals(date, model.getSortDate())) {
                    date = Optional.ofNullable(timeMap.get(date)).orElse(date);
                    dimIdColumn = getDimIdColumn(model.getDimId(), date);
                    request.add(getCustomizeNumberSearchRequest(indexNameInfo, dimIdColumn, model.getItems()));
                }
            }
        }
        if (StringUtils.equals("05", model.getType())) {
            String dimIdColumn = getDimIdColumn(model.getDimId(), model.getSortDate());
            request.add(getCustomizeDateSearchRequest(indexNameInfo, dimIdColumn, model.getItems()));
            for (String date : model.getDates()) {
                if (!StringUtils.equals(date, model.getSortDate())) {
                    dimIdColumn = getDimIdColumn(model.getDimId(), date);
                    request.add(getCustomizeDateSearchRequest(indexNameInfo, dimIdColumn, model.getItems()));
                }
            }
        }
        // 处理响应
        int sort = 0;
        boolean first = true;
        MultiSearchResponse multiSearchResponse =
                ESQueryUtils.getClient().multiSearch(request, RequestOptions.DEFAULT);
        for (MultiSearchResponse.Item item : multiSearchResponse.getResponses()) {
            SearchResponse response = item.getResponse();
            RangeResult result = new RangeResult();
            result.setMapping(new HashMap<>());
            SearchHits hits = response.getHits();
            result.setTotal(hits.getTotalHits().value);
            // 第一个一定是排序列结果
            if (first) {
                Aggregations aggregations = response.getAggregations();
                Range terms = aggregations.get("count");
                for (Range.Bucket entry : terms.getBuckets()) {
                    result.getMapping().put(entry.getKeyAsString(), entry);
                    sorted.add(entry.getKeyAsString());
                    titles.add(SortResult.of().setTitle(entry.getKeyAsString()).setCount(entry.getDocCount()));
                }
                map.put(model.getSortDate(), result);
                first = false;
                continue;
            }
            // 第一次已经处理了排序列 剩下的接着处理就行
            String date = model.getDates().get(sort++);
            if (StringUtils.equals(date, model.getSortDate())) {
                date = model.getDates().get(sort++);
            }
            Aggregations aggregations = response.getAggregations();
            Range terms = aggregations.get("count");
            for (Range.Bucket entry : terms.getBuckets()) {
                result.getMapping().put(entry.getKeyAsString(), entry);
                SortResult sortResult = SortResult.of().setTitle(entry.getKeyAsString()).setCount(entry.getDocCount());
                if (!sorted.contains(sortResult.getTitle())) {
                    sorted.add(sortResult.getTitle());
                    others.add(sortResult);
                }
            }
            map.put(date, result);
        }
        return map;
    }

    /**
     * 组装最终返回结果
     *
     * @param model 输入
     * @param data  结果集
     * @param total 总数
     * @return 响应
     */
    private DimMonitorDetailRsp getDimMonitorDetailRsp(DimMonitorDetailModel model, List<Map<String, Object>> data, int total) {
        if (total == 1) {
            Page<Map<String, Object>> page = Page.<Map<String, Object>>of().setContent(Collections.emptyList())
                    .setSize(model.getPageSize())
                    .setRequestPager(model.getPageNumber())
                    .setTotalElements(0L)
                    .setTotalPages(0);
            return DimMonitorDetailRsp.of().setData(page);
        }
        int pages = total / model.getPageSize();
        Page<Map<String, Object>> page = Page.<Map<String, Object>>of()
                .setContent(data)
                .setSize(model.getPageSize())
                .setRequestPager(model.getPageNumber())
                .setTotalElements((long) total)
                .setTotalPages(total % model.getPageSize() > 0 ? pages + 1 : pages);
        return DimMonitorDetailRsp.of().setData(page);
    }

    /**
     * 组装 分页数据
     *
     * @param model  输入
     * @param titles 标题名称
     * @param map    mapping
     * @param number 计数器
     * @param start  开始位置
     * @param end    结束位置
     * @param data   结果集
     * @return 计数器
     */
    private int getData(DimMonitorDetailModel model, Collection<String> titles, Map<String, Result> map, int number,
                        int start, int end, List<Map<String, Object>> data) {
        for (String title : titles) {
            if (number >= end) {
                break;
            }
            if (number++ < start) {
                continue;
            }
            // 查询到请求页
            Map<String, Object> row = new HashMap<>();
            String showName = Optional
                    .ofNullable((DimItemInfo) CacheUtils.getCacheInfo(CacheConfig.DIM_ITEM_INFO,
                            String.format(BaseParse.GROUP_FORMAT, model.getDimId(), title)))
                    .map(DimItemInfo::getDimItemName)
                    .orElse(title);
            row.put("title", showName);
            for (String date : model.getDates()) {
                Result result = map.get(date);
                Terms.Bucket bucket = result.getMapping().get(title);
                if (bucket == null) {
                    row.put(date, getDefaultInfo("0", "0%"));
                } else {
                    DimMonitorDetailRsp.Info info = getDefaultInfo(String.valueOf(bucket.getDocCount()), TermsUtils.getPercent(bucket.getDocCount(),
                            result.getTotal()));
                    row.put(date, info);
                }
            }
            data.add(row);
        }
        return number;
    }

    private DimMonitorDetailRsp.Info getDefaultInfo(String count, String percent) {
        DimMonitorDetailRsp.Info info = new DimMonitorDetailRsp.Info();
        info.setCount(count);
        info.setPercent(percent);
        return info;
    }

    private int getGroupData(DimMonitorDetailModel model, Collection<String> titles, Map<String, HistogramResult> map, int number,
                             int start, int end, List<Map<String, Object>> data) {
        for (String title : titles) {
            if (number >= end) {
                break;
            }
            if (number++ < start) {
                continue;
            }
            // 查询到请求页
            Map<String, Object> row = new HashMap<>();
            if (StringUtils.equals("03", model.getType())) {
                long key = ((Double) Double.parseDouble(title)).longValue();
                long high = key + ((Double) Double.parseDouble(model.getInterval())).longValue();
                String showName = key + "-" + high;
                row.put("title", showName);
            }
            if (StringUtils.equals("05", model.getType())) {
                String showName = getTitle(title, new DateHistogramInterval(model.getInterval()));
                row.put("title", showName);
            }
            for (String date : model.getDates()) {
                HistogramResult result = map.get(date);
                Histogram.Bucket bucket = result.getMapping().get(title);
                if (bucket == null) {
                    row.put(date, getDefaultInfo("0", "0%"));
                } else {
                    DimMonitorDetailRsp.Info info = getDefaultInfo(String.valueOf(bucket.getDocCount()), TermsUtils.getPercent(bucket.getDocCount(),
                            result.getTotal()));
                    row.put(date, info);
                }
            }
            data.add(row);
        }
        return number;
    }

    private int getCustomizeData(DimMonitorDetailModel model, Collection<SortResult> titles, Map<String, RangeResult> map,
                                 int number,
                                 int start, int end, List<Map<String, Object>> data) {
        for (SortResult title : titles) {
            if (number >= end) {
                break;
            }
            if (number++ < start) {
                continue;
            }
            // 查询到请求页
            Map<String, Object> row = new HashMap<>();
            row.put("title", title.getTitle());
            for (String date : model.getDates()) {
                RangeResult result = map.get(date);
                Range.Bucket bucket = result.getMapping().get(title.getTitle());
                if (bucket == null) {
                    row.put(date, getDefaultInfo("0", "0%"));
                } else {
                    DimMonitorDetailRsp.Info info = getDefaultInfo(String.valueOf(bucket.getDocCount()), TermsUtils.getPercent(bucket.getDocCount(),
                            result.getTotal()));
                    row.put(date, info);
                }
            }
            data.add(row);
        }
        return number;
    }

    /**
     * 第一页 第一行 数据
     *
     * @param model 输入
     * @param map   mapping
     * @param data  结果集
     */
    private void createTotalRow(DimMonitorDetailModel model, Map<String, Result> map, List<Map<String, Object>> data) {
        if (model.getPageNumber() == 1) {
            Map<String, Object> row = new HashMap<>();
            row.put("title", TOTAL_TITLE_STR);
            for (String date : model.getDates()) {
                Result result = map.get(date);
                DimMonitorDetailRsp.Info info = getDefaultInfo(result.getTotal() + "", "100.00%");
                row.put(date, info);
            }
            data.add(row);
        }
    }

    private void createGroupTotalRow(DimMonitorDetailModel model, Map<String, HistogramResult> map, List<Map<String, Object>> data) {
        if (model.getPageNumber() == 1) {
            Map<String, Object> row = new HashMap<>();
            row.put("title", TOTAL_TITLE_STR);
            for (String date : model.getDates()) {
                HistogramResult result = map.get(date);
                DimMonitorDetailRsp.Info info = getDefaultInfo(result.getTotal() + "", "100.00%");
                row.put(date, info);
            }
            data.add(row);
        }
    }

    private void createCustomizeTotalRow(DimMonitorDetailModel model, Map<String, RangeResult> map, List<Map<String, Object>> data) {
        if (model.getPageNumber() == 1) {
            Map<String, Object> row = new HashMap<>();
            row.put("title", TOTAL_TITLE_STR);
            for (String date : model.getDates()) {
                RangeResult result = map.get(date);
                DimMonitorDetailRsp.Info info = getDefaultInfo(result.getTotal() + "", "100.00%");
                row.put(date, info);
            }
            data.add(row);
        }
    }

    /**
     * 默认不排序
     *
     * @param model 输入
     * @return 响应
     */
    private DimMonitorDetailRsp defaultResult(DimMonitorDetailModel model) throws IOException {
        Set<String> titles = getSortTitlesByName(model);
        //根据主题从缓存中获取索引名
        Map<String, Result> map = getStringResultMap(model, titles);
        // 分页
        int start = getStart(model);
        int end = model.getPageNumber() * model.getPageSize() - 1;
        List<Map<String, Object>> data = new ArrayList<>();
        createTotalRow(model, map, data);
        getData(model, titles, map, 0, start, end, data);
        return getDimMonitorDetailRsp(model, data, titles.size() + 1);
    }

    private Set<String> getSortTitlesByName(DimMonitorDetailModel model) {
        boolean desc = StringUtils.equals("title", model.getSortDate()) && model.getSort() != null && !model.getSort();
        if (desc) {
            // 降序
            if (StringUtils.equals("03", model.getType())) {
                return new TreeSet<>((D1, D2) -> {
                    Double v1 = getValue(D1);
                    Double v2 = getValue(D2);
                    return v2.compareTo(v1);
                });
            }
            if (StringUtils.equals("05", model.getType())) {
                return new TreeSet<>((D1, D2) -> {
                    if (StringUtils.contains(D1, "-∞") || StringUtils.contains(D2, "+∞")) {
                        return 1;
                    }
                    if (StringUtils.contains(D1, "+∞") || StringUtils.contains(D2, "-∞")) {
                        return -1;
                    }
                    return StringUtils.compare(D2, D1);
                });
            }
            return new TreeSet<>(Comparator.reverseOrder());
        } else {
            // 默认升序
            if (StringUtils.equals("03", model.getType())) {
                return new TreeSet<>((D1, D2) -> {
                    Double v1 = getValue(D1);
                    Double v2 = getValue(D2);
                    return v1.compareTo(v2);
                });
            }
            if (StringUtils.equals("05", model.getType())) {
                return new TreeSet<>((D1, D2) -> {
                    if (StringUtils.contains(D1, "-∞") || StringUtils.contains(D2, "+∞")) {
                        return -1;
                    }
                    if (StringUtils.contains(D1, "+∞") || StringUtils.contains(D2, "-∞")) {
                        return 1;
                    }
                    return StringUtils.compare(D1, D2);
                });
            }
            return new TreeSet<>(Comparator.naturalOrder());
        }
    }

    private Set<SortResult> getSortResultsByCount(DimMonitorDetailModel model) {
        boolean desc = model.getSort() != null && !model.getSort();
        if (desc) {
            // 降序
            return new TreeSet<>((SortResult D1, SortResult D2) -> {
                int compare = NumberUtils.compare(D2.getCount(), D1.getCount());
                if (compare == 0) {
                    return 1;
                }
                return compare;
            });
        } else {
            // 默认升序
            return new TreeSet<>((SortResult D1, SortResult D2) -> {
                int compare = NumberUtils.compare(D1.getCount(), D2.getCount());
                if (compare == 0) {
                    return 1;
                }
                return compare;
            });
        }
    }

    /**
     * 默认分组 title排序
     *
     * @param model 输入
     * @return 响应
     */
    private DimMonitorDetailRsp getGroupResult(DimMonitorDetailModel model) throws IOException {
        Set<String> titles = getSortTitlesByName(model);
        //根据主题从缓存中获取索引名
        Map<String, HistogramResult> map = getGroupStringResultMap(model, titles);
        // 分页
        int start = getStart(model);
        int end = model.getPageNumber() * model.getPageSize() - 1;
        List<Map<String, Object>> data = new ArrayList<>();
        createGroupTotalRow(model, map, data);
        getGroupData(model, titles, map, 0, start, end, data);
        return getDimMonitorDetailRsp(model, data, titles.size() + 1);
    }

    private int getStart(DimMonitorDetailModel model) {
        return (model.getPageNumber() - 1) * model.getPageSize() - 1;
    }

    /**
     * 默认分组 title排序
     *
     * @param model 输入
     * @return 响应
     */
    private DimMonitorDetailRsp getCustomizeResult(DimMonitorDetailModel model) throws IOException {
        Set<SortResult> titles = getSortResultsByName(model);
        //根据主题从缓存中获取索引名
        Map<String, RangeResult> map = getCustomizeStringResultMap(model, titles);
        // 分页
        int start = getStart(model);
        int end = model.getPageNumber() * model.getPageSize() - 1;
        List<Map<String, Object>> data = new ArrayList<>();
        createCustomizeTotalRow(model, map, data);
        getCustomizeData(model, titles, map, 0, start, end, data);
        return getDimMonitorDetailRsp(model, data, titles.size() + 1);
    }

    private Set<SortResult> getSortResultsByName(DimMonitorDetailModel model) {
        boolean desc = StringUtils.equals("title", model.getSortDate()) && model.getSort() != null && !model.getSort();
        if (desc) {
            // 降序
            if (StringUtils.equals("03", model.getType())) {
                return new TreeSet<>((D1, D2) -> {
                    Double v1 = getValue(D1.getTitle());
                    Double v2 = getValue(D2.getTitle());
                    return v2.compareTo(v1);
                });
            }
            if (StringUtils.equals("05", model.getType())) {
                return new TreeSet<>((D1, D2) -> {
                    String title1 = D1.getTitle();
                    String title2 = D2.getTitle();
                    boolean equals = (StringUtils.contains(title1, "-∞") && StringUtils.contains(title2, "-∞"))
                            || (StringUtils.contains(title1, "+∞") && StringUtils.contains(title2, "+∞"));
                    if (equals) {
                        return 0;
                    }
                    if (StringUtils.contains(title1, "-∞") || StringUtils.contains(title2, "+∞")) {
                        return 1;
                    }
                    if (StringUtils.contains(title1, "+∞") || StringUtils.contains(title2, "-∞")) {
                        return -1;
                    }
                    return StringUtils.compare(title2, title1);
                });
            }
            return new TreeSet<>((D1, D2) -> StringUtils.compare(D1.getTitle(), D2.getTitle()));
        } else {
            // 默认升序
            if (StringUtils.equals("03", model.getType())) {
                return new TreeSet<>((D1, D2) -> {
                    Double v1 = getValue(D1.getTitle());
                    Double v2 = getValue(D2.getTitle());
                    return v1.compareTo(v2);
                });
            }
            if (StringUtils.equals("05", model.getType())) {
                return new TreeSet<>((D1, D2) -> {
                    String title1 = D1.getTitle();
                    String title2 = D2.getTitle();
                    boolean equals = (StringUtils.contains(title1, "-∞") && StringUtils.contains(title2, "-∞"))
                            || (StringUtils.contains(title1, "+∞") && StringUtils.contains(title2, "+∞"));
                    if (equals) {
                        return 0;
                    }
                    if (StringUtils.contains(title1, "-∞") || StringUtils.contains(title2, "+∞")) {
                        return -1;
                    }
                    if (StringUtils.contains(title1, "+∞") || StringUtils.contains(title2, "-∞")) {
                        return 1;
                    }
                    return StringUtils.compare(title1, title2);
                });
            }
            return new TreeSet<>((D1, D2) -> StringUtils.compare(D2.getTitle(), D1.getTitle()));
        }
    }

    /**
     * 统计结果map
     *
     * @param model  输入
     * @param titles 名称列表
     * @return mapping
     * @throws IOException es异常
     */
    private Map<String, Result> getStringResultMap(DimMonitorDetailModel model, Set<String> titles) throws IOException {

        Map<String, String> timeMap = esDateService.timeSymbolRelation("1");
        timeMap = Optional.ofNullable(timeMap).orElse(new HashMap<>());
        IndexNameInfo indexNameInfo = CacheUtils.getCacheInfo(CacheConfig.CONFIGURATION_INFO, "1");
        Map<String, Result> map = new HashMap<>();
        MultiSearchRequest request = new MultiSearchRequest();
        for (String date : model.getDates()) {
            date = Optional.ofNullable(timeMap.get(date)).orElse(date);
            request.add(getSearchRequest(indexNameInfo, model, date));
        }
        int sort = 0;
        MultiSearchResponse multiSearchResponse =
                ESQueryUtils.getClient().multiSearch(request, RequestOptions.DEFAULT);
        for (MultiSearchResponse.Item item : multiSearchResponse.getResponses()) {
            String date = model.getDates().get(sort++);
            SearchResponse response = item.getResponse();
            SearchHits hits = response.getHits();
            Result result = new Result();
            result.setMapping(new HashMap<>());
            result.setTotal(hits.getTotalHits().value);
            Aggregations aggregations = response.getAggregations();
            Terms terms = aggregations.get("count");
            for (Terms.Bucket entry : terms.getBuckets()) {
                result.getMapping().put(entry.getKeyAsString(), entry);
                titles.add(entry.getKeyAsString());
            }
            map.put(date, result);
        }
        return map;
    }

    /**
     * 默认分组
     *
     * @param model  输入
     * @param titles 标题名称
     * @return mapping
     * @throws IOException es异常
     */
    private Map<String, HistogramResult> getGroupStringResultMap(DimMonitorDetailModel model, Set<String> titles) throws IOException {

        Map<String, String> timeMap = esDateService.timeSymbolRelation("1");
        timeMap = Optional.ofNullable(timeMap).orElse(new HashMap<>());
        IndexNameInfo indexNameInfo = CacheUtils.getCacheInfo(CacheConfig.CONFIGURATION_INFO, "1");
        Map<String, HistogramResult> map = new HashMap<>();
        MultiSearchRequest request = new MultiSearchRequest();
        if (StringUtils.equals("03", model.getType())) {
            for (String date : model.getDates()) {
                date = Optional.ofNullable(timeMap.get(date)).orElse(date);
                request.add(getNumberSearchRequest(indexNameInfo, model, date));
            }
        }
        if (StringUtils.equals("05", model.getType())) {
            for (String date : model.getDates()) {
                date = Optional.ofNullable(timeMap.get(date)).orElse(date);
                request.add(getDateSearchRequest(indexNameInfo, model, date));
            }
        }
        int sort = 0;
        MultiSearchResponse multiSearchResponse =
                ESQueryUtils.getClient().multiSearch(request, RequestOptions.DEFAULT);
        for (MultiSearchResponse.Item item : multiSearchResponse.getResponses()) {
            String date = model.getDates().get(sort++);
            SearchResponse response = item.getResponse();
            SearchHits hits = response.getHits();
            HistogramResult result = new HistogramResult();
            result.setMapping(new HashMap<>());
            result.setTotal(hits.getTotalHits().value);
            Aggregations aggregations = response.getAggregations();
            Histogram terms = aggregations.get("count");
            for (Histogram.Bucket entry : terms.getBuckets()) {
                result.getMapping().put(entry.getKeyAsString(), entry);
                titles.add(entry.getKeyAsString());
            }
            map.put(date, result);
        }
        return map;
    }

    /**
     * 自定义
     *
     * @param model  输入
     * @param titles title名称列表
     * @return mapping
     * @throws IOException es异常
     */
    private Map<String, RangeResult> getCustomizeStringResultMap(DimMonitorDetailModel model, Set<SortResult> titles) throws IOException {

        Map<String, String> timeMap = esDateService.timeSymbolRelation("1");
        timeMap = Optional.ofNullable(timeMap).orElse(new HashMap<>());
        IndexNameInfo indexNameInfo = CacheUtils.getCacheInfo(CacheConfig.CONFIGURATION_INFO, "1");
        Map<String, RangeResult> map = new HashMap<>();
        MultiSearchRequest request = new MultiSearchRequest();
        if (StringUtils.equals("03", model.getType())) {
            for (String date : model.getDates()) {
                date = Optional.ofNullable(timeMap.get(date)).orElse(date);
                String dimIdColumn = getDimIdColumn(model.getDimId(), date);
                request.add(getCustomizeNumberSearchRequest(indexNameInfo, dimIdColumn, model.getItems()));
            }
        }
        if (StringUtils.equals("05", model.getType())) {
            for (String date : model.getDates()) {
                date = Optional.ofNullable(timeMap.get(date)).orElse(date);
                String dimIdColumn = getDimIdColumn(model.getDimId(), date);
                request.add(getCustomizeDateSearchRequest(indexNameInfo, dimIdColumn, model.getItems()));
            }
        }
        int sort = 0;
        MultiSearchResponse multiSearchResponse =
                ESQueryUtils.getClient().multiSearch(request, RequestOptions.DEFAULT);
        for (MultiSearchResponse.Item item : multiSearchResponse.getResponses()) {
            String date = model.getDates().get(sort++);
            SearchResponse response = item.getResponse();
            SearchHits hits = response.getHits();
            RangeResult result = new RangeResult();
            result.setMapping(new HashMap<>());
            result.setTotal(hits.getTotalHits().value);
            Aggregations aggregations = response.getAggregations();
            Range terms = aggregations.get("count");
            for (Range.Bucket entry : terms.getBuckets()) {
                result.getMapping().put(entry.getKeyAsString(), entry);
                titles.add(SortResult.of().setTitle(entry.getKeyAsString()).setCount(entry.getDocCount()));
            }
            map.put(date, result);
        }
        return map;
    }

    private SearchRequest getNumberSearchRequest(IndexNameInfo indexNameInfo, DimMonitorDetailModel model, String date) {
        BucketOrder bucketOrder = getBucketOrder(model);
        String dimIdColumn = getDimIdColumn(model.getDimId(), date);
        BoolQueryBuilder boolQueryBuilder = getBoolQueryBuilder(dimIdColumn);
        HistogramAggregationBuilder builder =
                AggregationBuilders.histogram("count").field(dimIdColumn).interval(Double.parseDouble(model.getInterval())).order(bucketOrder).minDocCount(1);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.aggregation(builder).query(boolQueryBuilder).trackTotalHits(true).size(0);
        return new SearchRequest(indexNameInfo.getDataDate())
                .types(indexNameInfo.getIndexType())
                .source(searchSourceBuilder);
    }

    private SearchRequest getDateSearchRequest(IndexNameInfo indexNameInfo, DimMonitorDetailModel model, String date) {
        BucketOrder bucketOrder = getBucketOrder(model);
        String dimIdColumn = getDimIdColumn(model.getDimId(), date);
        BoolQueryBuilder boolQueryBuilder = getBoolQueryBuilder(dimIdColumn);
        DateHistogramAggregationBuilder builder =
                AggregationBuilders.dateHistogram("count").field(dimIdColumn).dateHistogramInterval(new DateHistogramInterval(model.getInterval())).format(
                        "yyyy-MM-dd").order(bucketOrder).minDocCount(1);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.aggregation(builder).query(boolQueryBuilder).trackTotalHits(true).size(0);
        return new SearchRequest(indexNameInfo.getDataDate())
                .types(indexNameInfo.getIndexType())
                .source(searchSourceBuilder);
    }

    private SearchRequest getSearchRequest(IndexNameInfo indexNameInfo, DimMonitorDetailModel model, String date) {
        BucketOrder bucketOrder = getBucketOrder(model);
        String dimIdColumn = getDimIdColumn(model.getDimId(), date);
        BoolQueryBuilder boolQueryBuilder = getBoolQueryBuilder(dimIdColumn);
        TermsAggregationBuilder termsBuilder =
                AggregationBuilders.terms("count").field(dimIdColumn).order(bucketOrder).size(Integer.MAX_VALUE);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.aggregation(termsBuilder).query(boolQueryBuilder).trackTotalHits(true).size(0);
        return new SearchRequest(indexNameInfo.getDataDate())
                .types(indexNameInfo.getIndexType())
                .source(searchSourceBuilder);
    }

    @Data
    @NoArgsConstructor
    public static class Result {
        private long total;
        private Map<String, Terms.Bucket> mapping;
    }

    @Data
    @NoArgsConstructor
    public static class HistogramResult {
        private long total;
        private Map<String, Histogram.Bucket> mapping;
    }

    @Data
    @NoArgsConstructor
    public static class RangeResult {
        private long total;
        private Map<String, Range.Bucket> mapping;
    }

    @Getter
    @Setter
    @EqualsAndHashCode
    @Accessors(chain = true)
    @RequiredArgsConstructor(staticName = "of")
    public static class SortResult {
        @EqualsAndHashCode.Include
        private String title;
        private long count;
    }
}